/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package midi;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Vector;
import javax.swing.*;
import music.types.BaseNotes;
import music.types.Interval;
import music.types.MidiNote;
/**
 *
 * @author TJ
 */
public class Keyboard extends JPanel implements AWTEventListener{
    private MidiPlayer mp;
    private int channel=0;
    private static final HashMap<Character, NoteWrapper> notes;
    private static final OctaveManager upperHand,lowerHand;
    private HashMap<Character,Boolean> down;
    private Vector<NotePlayListener> playAction;
    static{
        upperHand=new OctaveManager(0);
        lowerHand=new OctaveManager(-1);
        notes=new HashMap<Character, NoteWrapper>();
       
        notes.put('z', new NoteWrapper(BaseNotes.C.getNote(),lowerHand));
        notes.put('s', new NoteWrapper(new MidiNote(61,"C#"),lowerHand));
        notes.put('x', new NoteWrapper(BaseNotes.D.getNote(),lowerHand));
        notes.put('d', new NoteWrapper(new MidiNote(63,"D#"),lowerHand));
        notes.put('c', new NoteWrapper(BaseNotes.E.getNote(),lowerHand));
        notes.put('v', new NoteWrapper(BaseNotes.F.getNote(),lowerHand));
        notes.put('g', new NoteWrapper(new MidiNote(66,"F#"),lowerHand));
        notes.put('b', new NoteWrapper(BaseNotes.G.getNote(),lowerHand));
        notes.put('h', new NoteWrapper(new MidiNote(68,"G#"),lowerHand));
        notes.put('n', new NoteWrapper(BaseNotes.A.getNote(),lowerHand));
        notes.put('j', new NoteWrapper(new MidiNote(70,"A#"),lowerHand));
        notes.put('m', new NoteWrapper(BaseNotes.B.getNote(),lowerHand));
        notes.put(',', new NoteWrapper(BaseNotes.C.getNote().shiftOctave(1, true),lowerHand));
        notes.put('l', new NoteWrapper(new MidiNote(73,"C#"),lowerHand));
        notes.put('.', new NoteWrapper(BaseNotes.D.getNote().shiftOctave(1, true),lowerHand));
        notes.put(';', new NoteWrapper(new MidiNote(75,"D#"),lowerHand));
        notes.put('/', new NoteWrapper(BaseNotes.E.getNote().shiftOctave(1, true),lowerHand));
        notes.put('q', new NoteWrapper(BaseNotes.C.getNote(),upperHand));
        notes.put('2', new NoteWrapper(new MidiNote(61,"C#"),upperHand));
        notes.put('w', new NoteWrapper(BaseNotes.D.getNote(),upperHand));
        notes.put('3', new NoteWrapper(new MidiNote(63,"D#"),upperHand));
        notes.put('e', new NoteWrapper(BaseNotes.E.getNote(),upperHand));
        notes.put('r', new NoteWrapper(BaseNotes.F.getNote(),upperHand));
        notes.put('5', new NoteWrapper(new MidiNote(66,"F#"),upperHand));
        notes.put('t', new NoteWrapper(BaseNotes.G.getNote(),upperHand));
        notes.put('6', new NoteWrapper(new MidiNote(68,"G#"),upperHand));
        notes.put('y', new NoteWrapper(BaseNotes.A.getNote(),upperHand));
        notes.put('7', new NoteWrapper(new MidiNote(70,"A#"),upperHand));
        notes.put('u', new NoteWrapper(BaseNotes.B.getNote(),upperHand));
        notes.put('i', new NoteWrapper(BaseNotes.C.getNote().shiftOctave(1, true),upperHand));
        notes.put('9', new NoteWrapper(new MidiNote(73,"C#"),upperHand));
        notes.put('o', new NoteWrapper(BaseNotes.D.getNote().shiftOctave(1, true),upperHand));
        notes.put('0', new NoteWrapper(new MidiNote(75,"D#"),upperHand));
        notes.put('p', new NoteWrapper(BaseNotes.E.getNote().shiftOctave(1, true),upperHand));
    }
    public Keyboard() {
        init();
    }
    private void init() {
        try{
       mp=MidiPlayer.getMidiPlayer();
       playAction=new Vector<NotePlayListener>();
        down=new HashMap<Character,Boolean>();
        Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
        }catch(Exception e) {}
    }
    public void addNotePlayedListener(NotePlayListener n){playAction.add(n);}
    public void removeNotePlayedListener(NotePlayListener n) {playAction.remove(n);}
    public Vector<MidiNote> getNotesPlaying() {
        Vector<MidiNote> note=new Vector<MidiNote>();
        for(Entry<Character, Boolean> e : down.entrySet()) {
            if(e.getValue().booleanValue()) {
                note.add(notes.get(e.getKey()).getNote());
            }
        }
        return note;
    }
    @Override
    public void eventDispatched(AWTEvent event) {
        if(event instanceof KeyEvent) {
            KeyEvent e=(KeyEvent) event;
            char c=Character.toLowerCase(e.getKeyChar());
            if(e.getID()==KeyEvent.KEY_RELEASED) {
            switch(e.getKeyCode()){
                case KeyEvent.VK_ALT:
                    //System.out.println(e.getKeyLocation());
                    if(e.getKeyLocation()==3) {
                        upperHand.decOctave();
                    }else if(e.getKeyLocation()==2){
                        lowerHand.incOctave();
                    }
                    break;
                case KeyEvent.VK_CONTROL:
                    //System.out.println(e.getKeyLocation());
                    if(e.getKeyLocation()==3){
                        upperHand.incOctave();
                    } else if(e.getKeyLocation()==2){
                        lowerHand.decOctave();
                    }
            }
            }
            if(!notes.containsKey(c)){return;}
            MidiNote playNote=notes.get(c).getNote();
            int n=playNote.getMidi();
            Boolean b2=down.get(c);
            boolean b=b2!=null && b2.booleanValue();
           
            switch(e.getID()){
                case KeyEvent.KEY_PRESSED:
                    down.put(c, true);
                    if(b) {return;}                    
                    mp.playNote(channel, n,255);
                    for(NotePlayListener npl : playAction) {
                        npl.noteStarted(playNote);
                    }
                    //System.out.println("PRESSED: "+Character.toLowerCase(e.getKeyChar()));
                    break;
                case KeyEvent.KEY_RELEASED:
                    down.put(c, false);
                    mp.stopNote(channel, n);
                    for(NotePlayListener npl : playAction) {
                        npl.noteStopped(playNote);
                    }
                    //System.out.println("RELEASED: "+Character.toLowerCase(e.getKeyChar()));
                    break;
            }
        }
    }
    public void changeInstrument(int newProgram) {
        mp.changeInstrument(channel,newProgram);
    }
    public void changeChannel(int c) {channel=c;}
    private static class OctaveManager {
        int octave;
        OctaveManager(int oct) {octave=oct;}
        public void incOctave() {octave++;}
        public void decOctave() {octave--;}
        public int getOctave() {return octave;}
    }
    private static class NoteWrapper {
        MidiNote note;
        OctaveManager oct;
        NoteWrapper(MidiNote n,OctaveManager m){oct=m;note=n;}
        public MidiNote getNote() {return new MidiNote(note.getMidi()+12*oct.getOctave(),note.getName());}
    }
}
